#include "CloudManager.h"

class TileManager;

struct cb_PS_Clouds_0
{	
	float cMix;
	UINT renderMode;
	float nodata2[2];			//16
};

CloudManager::CloudManager( vPlanet *_planet ) : TileManager( _planet ) {
	int len = strlen(objname)+7;
	char *texname = new char [len];
	strcpy_s( texname, len, objname );
	strcat_s( texname, len, "_cloud" );
	delete [ ] objname;
	objname = texname;

	maxlvl = min(	*(int*)gc->GetConfigParam( CFGPRM_SURFACEMAXLEVEL ),
					*(int*)oapiGetObjectParam( obj, OBJPRM_PLANET_SURFACEMAXLEVEL ) );
	maxbaselvl = min( 8, maxlvl );
	pcdir = _V( 1, 0, 0 );
	lightfac = (float)(*(double*)gc->GetConfigParam( CFGPRM_SURFACELIGHTBRT ));	
	nhitex = nhispec = nmask = 0;

	int maxidx = patchidx[maxbaselvl];
	tiledesc = new TILEDESC [maxidx];
	memset( tiledesc, 0, sizeof(TILEDESC)*maxidx );

	for( int j = 0; j < patchidx[maxbaselvl]; j++ )
		tiledesc[j].flag = 1;

	LoadTextures();
}

CloudManager::~CloudManager() {
	delete [ ] tiledesc;
}

void CloudManager::SaveParams( D3DXMATRIX *wmat, double scale, int level, double viewap ) {
	RP = &RP0;
	TileManager::SaveParams( wmat, scale, level, viewap );
}

void CloudManager::SaveParamsShadows( D3DXMATRIX *wmat, double scale, int level, double viewap, float shadow_alpha ) {
	RP = &RP1;
	TileManager::SaveParams( wmat, scale, level, viewap );
}

void CloudManager::Render( bool above ) {
	const float BlendFactors[4] = { 1.0f };

	ATM_FOG_PARAMS *AP = planet->GetAtmParams();
	VSCB_Clouds_0.Ambient0 = AP->Ambient0;
	VSCB_Clouds_0.Dispersion = AP->Dispersion;
	VSCB_Clouds_0.GlobalAmb = AP->GlobalAmb;
	VSCB_Clouds_0.SunAppRad = AP->SunAppRad;
	VSCB_Clouds_0.SunDir = AP->SunDir;
	/*auto mROT = planet->GetRotationMatrix();
	auto sunDir = _V(VSCB_Clouds_0.SunDir.x, VSCB_Clouds_0.SunDir.y, VSCB_Clouds_0.SunDir.z);
	sunDir = mul( mROT, sunDir);
	VSCB_Clouds_0.SunDir.x = sunDir.x;
	VSCB_Clouds_0.SunDir.y = sunDir.y;
	VSCB_Clouds_0.SunDir.z = sunDir.z;*/
	dCtx->UpdateSubresource( cb_VS_Clouds[0], 0, NULL, &VSCB_Clouds_0, 0, 0 );
	dCtx->VSSetConstantBuffers( 0, 2, cb_VS_Clouds );

	dCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	dCtx->IASetInputLayout( IL_TileVertex );
	dCtx->VSSetShader( VS_Clouds, NULL, NULL );
	cb_PS_Clouds_0 data;
	ZeroMemory(&data, sizeof(cb_PS_Clouds_0));

	if( microtex && microlvl > 0.01 ) {
		dCtx->PSSetShader( PS_Clouds[1], NULL, NULL );
		dCtx->PSSetSamplers( 1, 1, &SS_Clouds_micro );
		dCtx->PSSetShaderResources( 1, 1, &microtex );

		data.cMix = microlvl;

	}
	else
	{
		dCtx->PSSetShader( PS_Clouds[0], NULL, NULL );
	}
	data.renderMode = above ? 1 : 0;
	
	dCtx->UpdateSubresource( cb_PS_Clouds, 0, NULL, &data, 0, 0 );
	dCtx->PSSetConstantBuffers( 0, 1, &cb_PS_Clouds );

	dCtx->PSSetSamplers( 0, 1, &SS_Clouds );
	dCtx->OMSetBlendState( BS_SrcAlpha, BlendFactors, 0xFFFFFFFF );
	iCtx->RSSetState( above ? RS_CullBack_Solid : RS_CullFront_Solid );

	RP = &RP0;
	TileManager::Render(/**wmat, scale, level, viewap*/ );

	if (!above)
	{
		//restore RS
		iCtx->RSSetState( RS_CullBack_Solid );
	}
}

void CloudManager::InitRenderTile() {
/*	nothing	to do */
	memcpy( &M[1], SC->GetVP(), 64 );
}

void CloudManager::EndRenderTile() {
/*	nothing	to do */
}

void CloudManager::RenderSimple( int lvl, int npatch, TILEDESC *tile, D3DXMATRIX *mWorld ) {
	const UINT TileVertexStride = 40, VBOffset = 0;

	memcpy( &M[0], mWorld, 64 );
	memcpy( &M[1], SC->GetVP(), 64 );
	dCtx->UpdateSubresource( cb_VS_Clouds[1], 0, NULL, M, 0, 0 );

	for( int idx = 0; idx < npatch; idx++ ) {
		TILEMESH &mesh = TPL[lvl][idx];

		dCtx->PSSetShaderResources( 0, 1, &tile[idx].tex );
		dCtx->IASetVertexBuffers( 0, 1, &mesh.VB, &TileVertexStride, &VBOffset );
		dCtx->IASetIndexBuffer( mesh.IB, DXGI_FORMAT_R16_UINT, 0 );
		dCtx->DrawIndexed( mesh.nidx, 0, 0 );
	}
}

void CloudManager::RenderTile( int hemisphere, int lvl, int ilat, int nlat, int ilng, int nlng,/* double sdist,*/ TILEDESC *tile,
		const TEXCRDRANGE &range, ID3D11ShaderResourceView *tex, ID3D11ShaderResourceView *ltex, DWORD flag )
{
	const UINT TileVertexStride = 40, VBOffset = 0;

	TILEMESH &mesh = TPL[lvl][ilat];

	memcpy( M, &mWorld, 64 );
	dCtx->UpdateSubresource( cb_VS_Clouds[1], 0, NULL, M, 0, 0 );

	dCtx->PSSetShaderResources( 0, 1, &tex );
	dCtx->IASetVertexBuffers( 0, 1, &mesh.VB, &TileVertexStride, &VBOffset );
	dCtx->IASetIndexBuffer( mesh.IB, DXGI_FORMAT_R16_UINT, 0 );
	dCtx->DrawIndexed( mesh.nidx, 0, 0 );
}

void CloudManager::RenderShadow() {

	ATM_FOG_PARAMS *AP = planet->GetAtmParams();
	VSCB_CloudsShadows_0.FogDensity = AP->FogDensity;
	VSCB_CloudsShadows_0.HazeMode = AP->HazeMode;
	dCtx->UpdateSubresource( cb_VS_CloudsShadow_0, 0, NULL, &VSCB_CloudsShadows_0, 0, 0 );
	dCtx->VSSetConstantBuffers( 0, 1, &cb_VS_CloudsShadow_0 );
	dCtx->VSSetConstantBuffers( 1, 1, &cb_VS_Clouds[1] );

	dCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	dCtx->IASetInputLayout( IL_TileVertex );
	dCtx->VSSetShader( VS_CloudsShadow, NULL, NULL );
	if( microtex && microlvl > 0.01 ) {
		dCtx->PSSetShader( PS_CloudsShadow[1], NULL, NULL );
		dCtx->PSSetSamplers( 1, 1, &SS_Clouds_micro );
		dCtx->PSSetShaderResources( 1, 1, &microtex );

		dCtx->UpdateSubresource( cb_PS_Clouds, 0, NULL, &microlvl, 0, 0 );
		dCtx->PSSetConstantBuffers( 0, 1, &cb_PS_Clouds );
	}
	else
		dCtx->PSSetShader( PS_CloudsShadow[0], NULL, NULL );

	dCtx->PSSetSamplers( 0, 1, &SS_Clouds );
	dCtx->OMSetBlendState( BS_SrcAlpha_Blend_0, D3DXCOLOR( 1, 1, 1, 1 ), 0xFFFFFFFF );
	dCtx->OMSetDepthStencilState( DSS_NoDepth_NoStencil, 0 );

	RP = &RP1;
	TileManager::Render( /**wmat, scale, level, viewap*/ );
}